// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/compiler/js-typed-lowering.h"

#include "src/ast/modules.h"
#include "src/builtins/builtins-utils.h"
#include "src/code-factory.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/allocation-builder.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/operator-properties.h"
#include "src/compiler/type-cache.h"
#include "src/compiler/types.h"
#include "src/objects-inl.h"
#include "src/objects/js-generator.h"
#include "src/objects/module-inl.h"

namespace v8 {
namespace internal {
    namespace compiler {

        // A helper class to simplify the process of reducing a single binop node with a
        // JSOperator. This class manages the rewriting of context, control, and effect
        // dependencies during lowering of a binop and contains numerous helper
        // functions for matching the types of inputs to an operation.
        class JSBinopReduction final {
        public:
            JSBinopReduction(JSTypedLowering* lowering, Node* node)
                : lowering_(lowering)
                , node_(node)
            {
            }

            bool GetCompareNumberOperationHint(NumberOperationHint* hint)
            {
                DCHECK_EQ(1, node_->op()->EffectOutputCount());
                switch (CompareOperationHintOf(node_->op())) {
                case CompareOperationHint::kSignedSmall:
                    *hint = NumberOperationHint::kSignedSmall;
                    return true;
                case CompareOperationHint::kNumber:
                    *hint = NumberOperationHint::kNumber;
                    return true;
                case CompareOperationHint::kNumberOrOddball:
                    *hint = NumberOperationHint::kNumberOrOddball;
                    return true;
                case CompareOperationHint::kAny:
                case CompareOperationHint::kNone:
                case CompareOperationHint::kString:
                case CompareOperationHint::kSymbol:
                case CompareOperationHint::kBigInt:
                case CompareOperationHint::kReceiver:
                case CompareOperationHint::kReceiverOrNullOrUndefined:
                case CompareOperationHint::kInternalizedString:
                    break;
                }
                return false;
            }

            bool IsInternalizedStringCompareOperation()
            {
                DCHECK_EQ(1, node_->op()->EffectOutputCount());
                return (CompareOperationHintOf(node_->op()) == CompareOperationHint::kInternalizedString) && BothInputsMaybe(Type::InternalizedString());
            }

            bool IsReceiverCompareOperation()
            {
                DCHECK_EQ(1, node_->op()->EffectOutputCount());
                return (CompareOperationHintOf(node_->op()) == CompareOperationHint::kReceiver) && BothInputsMaybe(Type::Receiver());
            }

            bool IsReceiverOrNullOrUndefinedCompareOperation()
            {
                DCHECK_EQ(1, node_->op()->EffectOutputCount());
                return (CompareOperationHintOf(node_->op()) == CompareOperationHint::kReceiverOrNullOrUndefined) && BothInputsMaybe(Type::ReceiverOrNullOrUndefined());
            }

            bool IsStringCompareOperation()
            {
                DCHECK_EQ(1, node_->op()->EffectOutputCount());
                return (CompareOperationHintOf(node_->op()) == CompareOperationHint::kString) && BothInputsMaybe(Type::String());
            }

            bool IsSymbolCompareOperation()
            {
                DCHECK_EQ(1, node_->op()->EffectOutputCount());
                return (CompareOperationHintOf(node_->op()) == CompareOperationHint::kSymbol) && BothInputsMaybe(Type::Symbol());
            }

            // Check if a string addition will definitely result in creating a ConsString,
            // i.e. if the combined length of the resulting string exceeds the ConsString
            // minimum length.
            bool ShouldCreateConsString()
            {
                DCHECK_EQ(IrOpcode::kJSAdd, node_->opcode());
                DCHECK(OneInputIs(Type::String()));
                if (BothInputsAre(Type::String()) || BinaryOperationHintOf(node_->op()) == BinaryOperationHint::kString) {
                    HeapObjectBinopMatcher m(node_);
                    JSHeapBroker* broker = lowering_->broker();
                    if (m.right().HasValue() && m.right().Ref(broker).IsString()) {
                        StringRef right_string = m.right().Ref(broker).AsString();
                        if (right_string.length() >= ConsString::kMinLength)
                            return true;
                    }
                    if (m.left().HasValue() && m.left().Ref(broker).IsString()) {
                        StringRef left_string = m.left().Ref(broker).AsString();
                        if (left_string.length() >= ConsString::kMinLength) {
                            // The invariant for ConsString requires the left hand side to be
                            // a sequential or external string if the right hand side is the
                            // empty string. Since we don't know anything about the right hand
                            // side here, we must ensure that the left hand side satisfy the
                            // constraints independent of the right hand side.
                            return left_string.IsSeqString() || left_string.IsExternalString();
                        }
                    }
                }
                return false;
            }

            // Inserts a CheckReceiver for the left input.
            void CheckLeftInputToReceiver()
            {
                Node* left_input = graph()->NewNode(simplified()->CheckReceiver(), left(),
                    effect(), control());
                node_->ReplaceInput(0, left_input);
                update_effect(left_input);
            }

            // Inserts a CheckReceiverOrNullOrUndefined for the left input.
            void CheckLeftInputToReceiverOrNullOrUndefined()
            {
                Node* left_input = graph()->NewNode(simplified()->CheckReceiverOrNullOrUndefined(), left(),
                    effect(), control());
                node_->ReplaceInput(0, left_input);
                update_effect(left_input);
            }

            // Checks that both inputs are Receiver, and if we don't know
            // statically that one side is already a Receiver, insert a
            // CheckReceiver node.
            void CheckInputsToReceiver()
            {
                if (!left_type().Is(Type::Receiver())) {
                    CheckLeftInputToReceiver();
                }
                if (!right_type().Is(Type::Receiver())) {
                    Node* right_input = graph()->NewNode(simplified()->CheckReceiver(),
                        right(), effect(), control());
                    node_->ReplaceInput(1, right_input);
                    update_effect(right_input);
                }
            }

            // Checks that both inputs are Receiver, Null or Undefined and if
            // we don't know statically that one side is already a Receiver,
            // Null or Undefined, insert CheckReceiverOrNullOrUndefined nodes.
            void CheckInputsToReceiverOrNullOrUndefined()
            {
                if (!left_type().Is(Type::ReceiverOrNullOrUndefined())) {
                    CheckLeftInputToReceiverOrNullOrUndefined();
                }
                if (!right_type().Is(Type::ReceiverOrNullOrUndefined())) {
                    Node* right_input = graph()->NewNode(simplified()->CheckReceiverOrNullOrUndefined(),
                        right(), effect(), control());
                    node_->ReplaceInput(1, right_input);
                    update_effect(right_input);
                }
            }

            // Inserts a CheckSymbol for the left input.
            void CheckLeftInputToSymbol()
            {
                Node* left_input = graph()->NewNode(simplified()->CheckSymbol(), left(),
                    effect(), control());
                node_->ReplaceInput(0, left_input);
                update_effect(left_input);
            }

            // Checks that both inputs are Symbol, and if we don't know
            // statically that one side is already a Symbol, insert a
            // CheckSymbol node.
            void CheckInputsToSymbol()
            {
                if (!left_type().Is(Type::Symbol())) {
                    CheckLeftInputToSymbol();
                }
                if (!right_type().Is(Type::Symbol())) {
                    Node* right_input = graph()->NewNode(simplified()->CheckSymbol(), right(),
                        effect(), control());
                    node_->ReplaceInput(1, right_input);
                    update_effect(right_input);
                }
            }

            // Checks that both inputs are String, and if we don't know
            // statically that one side is already a String, insert a
            // CheckString node.
            void CheckInputsToString()
            {
                if (!left_type().Is(Type::String())) {
                    Node* left_input = graph()->NewNode(simplified()->CheckString(VectorSlotPair()), left(),
                        effect(), control());
                    node_->ReplaceInput(0, left_input);
                    update_effect(left_input);
                }
                if (!right_type().Is(Type::String())) {
                    Node* right_input = graph()->NewNode(simplified()->CheckString(VectorSlotPair()), right(),
                        effect(), control());
                    node_->ReplaceInput(1, right_input);
                    update_effect(right_input);
                }
            }

            // Checks that both inputs are InternalizedString, and if we don't know
            // statically that one side is already an InternalizedString, insert a
            // CheckInternalizedString node.
            void CheckInputsToInternalizedString()
            {
                if (!left_type().Is(Type::UniqueName())) {
                    Node* left_input = graph()->NewNode(
                        simplified()->CheckInternalizedString(), left(), effect(), control());
                    node_->ReplaceInput(0, left_input);
                    update_effect(left_input);
                }
                if (!right_type().Is(Type::UniqueName())) {
                    Node* right_input = graph()->NewNode(simplified()->CheckInternalizedString(), right(),
                        effect(), control());
                    node_->ReplaceInput(1, right_input);
                    update_effect(right_input);
                }
            }

            void ConvertInputsToNumber()
            {
                DCHECK(left_type().Is(Type::PlainPrimitive()));
                DCHECK(right_type().Is(Type::PlainPrimitive()));
                node_->ReplaceInput(0, ConvertPlainPrimitiveToNumber(left()));
                node_->ReplaceInput(1, ConvertPlainPrimitiveToNumber(right()));
            }

            void ConvertInputsToUI32(Signedness left_signedness,
                Signedness right_signedness)
            {
                node_->ReplaceInput(0, ConvertToUI32(left(), left_signedness));
                node_->ReplaceInput(1, ConvertToUI32(right(), right_signedness));
            }

            void SwapInputs()
            {
                Node* l = left();
                Node* r = right();
                node_->ReplaceInput(0, r);
                node_->ReplaceInput(1, l);
            }

            // Remove all effect and control inputs and outputs to this node and change
            // to the pure operator {op}.
            Reduction ChangeToPureOperator(const Operator* op, Type type = Type::Any())
            {
                DCHECK_EQ(0, op->EffectInputCount());
                DCHECK_EQ(false, OperatorProperties::HasContextInput(op));
                DCHECK_EQ(0, op->ControlInputCount());
                DCHECK_EQ(2, op->ValueInputCount());

                // Remove the effects from the node, and update its effect/control usages.
                if (node_->op()->EffectInputCount() > 0) {
                    lowering_->RelaxEffectsAndControls(node_);
                }
                // Remove the inputs corresponding to context, effect, and control.
                NodeProperties::RemoveNonValueInputs(node_);
                // Finally, update the operator to the new one.
                NodeProperties::ChangeOp(node_, op);

                // TODO(jarin): Replace the explicit typing hack with a call to some method
                // that encapsulates changing the operator and re-typing.
                Type node_type = NodeProperties::GetType(node_);
                NodeProperties::SetType(node_, Type::Intersect(node_type, type, zone()));

                return lowering_->Changed(node_);
            }

            Reduction ChangeToSpeculativeOperator(const Operator* op, Type upper_bound)
            {
                DCHECK_EQ(1, op->EffectInputCount());
                DCHECK_EQ(1, op->EffectOutputCount());
                DCHECK_EQ(false, OperatorProperties::HasContextInput(op));
                DCHECK_EQ(1, op->ControlInputCount());
                DCHECK_EQ(0, op->ControlOutputCount());
                DCHECK_EQ(0, OperatorProperties::GetFrameStateInputCount(op));
                DCHECK_EQ(2, op->ValueInputCount());

                DCHECK_EQ(1, node_->op()->EffectInputCount());
                DCHECK_EQ(1, node_->op()->EffectOutputCount());
                DCHECK_EQ(1, node_->op()->ControlInputCount());
                DCHECK_EQ(2, node_->op()->ValueInputCount());

                // Reconnect the control output to bypass the IfSuccess node and
                // possibly disconnect from the IfException node.
                lowering_->RelaxControls(node_);

                // Remove the frame state and the context.
                if (OperatorProperties::HasFrameStateInput(node_->op())) {
                    node_->RemoveInput(NodeProperties::FirstFrameStateIndex(node_));
                }
                node_->RemoveInput(NodeProperties::FirstContextIndex(node_));

                NodeProperties::ChangeOp(node_, op);

                // Update the type to number.
                Type node_type = NodeProperties::GetType(node_);
                NodeProperties::SetType(node_,
                    Type::Intersect(node_type, upper_bound, zone()));

                return lowering_->Changed(node_);
            }

            const Operator* NumberOp()
            {
                switch (node_->opcode()) {
                case IrOpcode::kJSAdd:
                    return simplified()->NumberAdd();
                case IrOpcode::kJSSubtract:
                    return simplified()->NumberSubtract();
                case IrOpcode::kJSMultiply:
                    return simplified()->NumberMultiply();
                case IrOpcode::kJSDivide:
                    return simplified()->NumberDivide();
                case IrOpcode::kJSModulus:
                    return simplified()->NumberModulus();
                case IrOpcode::kJSExponentiate:
                    return simplified()->NumberPow();
                case IrOpcode::kJSBitwiseAnd:
                    return simplified()->NumberBitwiseAnd();
                case IrOpcode::kJSBitwiseOr:
                    return simplified()->NumberBitwiseOr();
                case IrOpcode::kJSBitwiseXor:
                    return simplified()->NumberBitwiseXor();
                case IrOpcode::kJSShiftLeft:
                    return simplified()->NumberShiftLeft();
                case IrOpcode::kJSShiftRight:
                    return simplified()->NumberShiftRight();
                case IrOpcode::kJSShiftRightLogical:
                    return simplified()->NumberShiftRightLogical();
                default:
                    break;
                }
                UNREACHABLE();
            }

            bool LeftInputIs(Type t) { return left_type().Is(t); }

            bool RightInputIs(Type t) { return right_type().Is(t); }

            bool OneInputIs(Type t) { return LeftInputIs(t) || RightInputIs(t); }

            bool BothInputsAre(Type t) { return LeftInputIs(t) && RightInputIs(t); }

            bool BothInputsMaybe(Type t)
            {
                return left_type().Maybe(t) && right_type().Maybe(t);
            }

            bool OneInputCannotBe(Type t)
            {
                return !left_type().Maybe(t) || !right_type().Maybe(t);
            }

            bool NeitherInputCanBe(Type t)
            {
                return !left_type().Maybe(t) && !right_type().Maybe(t);
            }

            Node* effect() { return NodeProperties::GetEffectInput(node_); }
            Node* control() { return NodeProperties::GetControlInput(node_); }
            Node* context() { return NodeProperties::GetContextInput(node_); }
            Node* left() { return NodeProperties::GetValueInput(node_, 0); }
            Node* right() { return NodeProperties::GetValueInput(node_, 1); }
            Type left_type() { return NodeProperties::GetType(node_->InputAt(0)); }
            Type right_type() { return NodeProperties::GetType(node_->InputAt(1)); }
            Type type() { return NodeProperties::GetType(node_); }

            SimplifiedOperatorBuilder* simplified() { return lowering_->simplified(); }
            Graph* graph() const { return lowering_->graph(); }
            JSGraph* jsgraph() { return lowering_->jsgraph(); }
            Isolate* isolate() { return jsgraph()->isolate(); }
            JSOperatorBuilder* javascript() { return lowering_->javascript(); }
            CommonOperatorBuilder* common() { return jsgraph()->common(); }
            Zone* zone() const { return graph()->zone(); }

        private:
            JSTypedLowering* lowering_; // The containing lowering instance.
            Node* node_; // The original node.

            Node* ConvertPlainPrimitiveToNumber(Node* node)
            {
                DCHECK(NodeProperties::GetType(node).Is(Type::PlainPrimitive()));
                // Avoid inserting too many eager ToNumber() operations.
                Reduction const reduction = lowering_->ReduceJSToNumberInput(node);
                if (reduction.Changed())
                    return reduction.replacement();
                if (NodeProperties::GetType(node).Is(Type::Number())) {
                    return node;
                }
                return graph()->NewNode(simplified()->PlainPrimitiveToNumber(), node);
            }

            Node* ConvertToUI32(Node* node, Signedness signedness)
            {
                // Avoid introducing too many eager NumberToXXnt32() operations.
                Type type = NodeProperties::GetType(node);
                if (signedness == kSigned) {
                    if (!type.Is(Type::Signed32())) {
                        node = graph()->NewNode(simplified()->NumberToInt32(), node);
                    }
                } else {
                    DCHECK_EQ(kUnsigned, signedness);
                    if (!type.Is(Type::Unsigned32())) {
                        node = graph()->NewNode(simplified()->NumberToUint32(), node);
                    }
                }
                return node;
            }

            void update_effect(Node* effect)
            {
                NodeProperties::ReplaceEffectInput(node_, effect);
            }
        };

        // TODO(turbofan): js-typed-lowering improvements possible
        // - immediately put in type bounds for all new nodes
        // - relax effects from generic but not-side-effecting operations

        JSTypedLowering::JSTypedLowering(Editor* editor, JSGraph* jsgraph,
            JSHeapBroker* broker, Zone* zone)
            : AdvancedReducer(editor)
            , jsgraph_(jsgraph)
            , broker_(broker)
            , empty_string_type_(Type::HeapConstant(broker, factory()->empty_string(),
                  graph()->zone()))
            , pointer_comparable_type_(
                  Type::Union(Type::Oddball(),
                      Type::Union(Type::SymbolOrReceiver(), empty_string_type_,
                          graph()->zone()),
                      graph()->zone()))
            , type_cache_(TypeCache::Get())
        {
        }

        Reduction JSTypedLowering::ReduceJSBitwiseNot(Node* node)
        {
            Node* input = NodeProperties::GetValueInput(node, 0);
            Type input_type = NodeProperties::GetType(input);
            if (input_type.Is(Type::PlainPrimitive())) {
                // JSBitwiseNot(x) => NumberBitwiseXor(ToInt32(x), -1)
                node->InsertInput(graph()->zone(), 1, jsgraph()->SmiConstant(-1));
                NodeProperties::ChangeOp(node, javascript()->BitwiseXor());
                JSBinopReduction r(this, node);
                r.ConvertInputsToNumber();
                r.ConvertInputsToUI32(kSigned, kSigned);
                return r.ChangeToPureOperator(r.NumberOp(), Type::Signed32());
            }
            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSDecrement(Node* node)
        {
            Node* input = NodeProperties::GetValueInput(node, 0);
            Type input_type = NodeProperties::GetType(input);
            if (input_type.Is(Type::PlainPrimitive())) {
                // JSDecrement(x) => NumberSubtract(ToNumber(x), 1)
                node->InsertInput(graph()->zone(), 1, jsgraph()->OneConstant());
                NodeProperties::ChangeOp(node, javascript()->Subtract());
                JSBinopReduction r(this, node);
                r.ConvertInputsToNumber();
                DCHECK_EQ(simplified()->NumberSubtract(), r.NumberOp());
                return r.ChangeToPureOperator(r.NumberOp(), Type::Number());
            }
            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSIncrement(Node* node)
        {
            Node* input = NodeProperties::GetValueInput(node, 0);
            Type input_type = NodeProperties::GetType(input);
            if (input_type.Is(Type::PlainPrimitive())) {
                // JSIncrement(x) => NumberAdd(ToNumber(x), 1)
                node->InsertInput(graph()->zone(), 1, jsgraph()->OneConstant());
                BinaryOperationHint hint = BinaryOperationHint::kAny; // Dummy.
                NodeProperties::ChangeOp(node, javascript()->Add(hint));
                JSBinopReduction r(this, node);
                r.ConvertInputsToNumber();
                DCHECK_EQ(simplified()->NumberAdd(), r.NumberOp());
                return r.ChangeToPureOperator(r.NumberOp(), Type::Number());
            }
            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSNegate(Node* node)
        {
            Node* input = NodeProperties::GetValueInput(node, 0);
            Type input_type = NodeProperties::GetType(input);
            if (input_type.Is(Type::PlainPrimitive())) {
                // JSNegate(x) => NumberMultiply(ToNumber(x), -1)
                node->InsertInput(graph()->zone(), 1, jsgraph()->SmiConstant(-1));
                NodeProperties::ChangeOp(node, javascript()->Multiply());
                JSBinopReduction r(this, node);
                r.ConvertInputsToNumber();
                return r.ChangeToPureOperator(r.NumberOp(), Type::Number());
            }
            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSAdd(Node* node)
        {
            JSBinopReduction r(this, node);
            if (r.BothInputsAre(Type::Number())) {
                // JSAdd(x:number, y:number) => NumberAdd(x, y)
                return r.ChangeToPureOperator(simplified()->NumberAdd(), Type::Number());
            }
            if (r.BothInputsAre(Type::PlainPrimitive()) && r.NeitherInputCanBe(Type::StringOrReceiver())) {
                // JSAdd(x:-string, y:-string) => NumberAdd(ToNumber(x), ToNumber(y))
                r.ConvertInputsToNumber();
                return r.ChangeToPureOperator(simplified()->NumberAdd(), Type::Number());
            }

            // Strength-reduce if one input is already known to be a string.
            if (r.LeftInputIs(Type::String())) {
                // JSAdd(x:string, y) => JSAdd(x, JSToString(y))
                Reduction const reduction = ReduceJSToStringInput(r.right());
                if (reduction.Changed()) {
                    NodeProperties::ReplaceValueInput(node, reduction.replacement(), 1);
                }
            } else if (r.RightInputIs(Type::String())) {
                // JSAdd(x, y:string) => JSAdd(JSToString(x), y)
                Reduction const reduction = ReduceJSToStringInput(r.left());
                if (reduction.Changed()) {
                    NodeProperties::ReplaceValueInput(node, reduction.replacement(), 0);
                }
            }

            // Always bake in String feedback into the graph.
            if (BinaryOperationHintOf(node->op()) == BinaryOperationHint::kString) {
                r.CheckInputsToString();
            }

            // Strength-reduce concatenation of empty strings if both sides are
            // primitives, as in that case the ToPrimitive on the other side is
            // definitely going to be a no-op.
            if (r.BothInputsAre(Type::Primitive())) {
                if (r.LeftInputIs(empty_string_type_)) {
                    // JSAdd("", x:primitive) => JSToString(x)
                    NodeProperties::ReplaceValueInputs(node, r.right());
                    NodeProperties::ChangeOp(node, javascript()->ToString());
                    NodeProperties::SetType(
                        node, Type::Intersect(r.type(), Type::String(), graph()->zone()));
                    Reduction const reduction = ReduceJSToString(node);
                    return reduction.Changed() ? reduction : Changed(node);
                } else if (r.RightInputIs(empty_string_type_)) {
                    // JSAdd(x:primitive, "") => JSToString(x)
                    NodeProperties::ReplaceValueInputs(node, r.left());
                    NodeProperties::ChangeOp(node, javascript()->ToString());
                    NodeProperties::SetType(
                        node, Type::Intersect(r.type(), Type::String(), graph()->zone()));
                    Reduction const reduction = ReduceJSToString(node);
                    return reduction.Changed() ? reduction : Changed(node);
                }
            }

            // Lower to string addition if both inputs are known to be strings.
            if (r.BothInputsAre(Type::String())) {
                Node* context = NodeProperties::GetContextInput(node);
                Node* frame_state = NodeProperties::GetFrameStateInput(node);
                Node* effect = NodeProperties::GetEffectInput(node);
                Node* control = NodeProperties::GetControlInput(node);

                // Compute the resulting length.
                Node* left_length = graph()->NewNode(simplified()->StringLength(), r.left());
                Node* right_length = graph()->NewNode(simplified()->StringLength(), r.right());
                Node* length = graph()->NewNode(simplified()->NumberAdd(), left_length, right_length);

                CellRef string_length_protector(broker(),
                    factory()->string_length_protector());
                if (string_length_protector.value().AsSmi() == Isolate::kProtectorValid) {
                    // We can just deoptimize if the {length} is out-of-bounds. Besides
                    // generating a shorter code sequence than the version below, this
                    // has the additional benefit of not holding on to the lazy {frame_state}
                    // and thus potentially reduces the number of live ranges and allows for
                    // more truncations.
                    length = effect = graph()->NewNode(
                        simplified()->CheckBounds(VectorSlotPair()), length,
                        jsgraph()->Constant(String::kMaxLength + 1), effect, control);
                } else {
                    // Check if we would overflow the allowed maximum string length.
                    Node* check = graph()->NewNode(simplified()->NumberLessThanOrEqual(), length,
                        jsgraph()->Constant(String::kMaxLength));
                    Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
                    Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
                    Node* efalse = effect;
                    {
                        // Throw a RangeError in case of overflow.
                        Node* vfalse = efalse = if_false = graph()->NewNode(
                            javascript()->CallRuntime(Runtime::kThrowInvalidStringLength),
                            context, frame_state, efalse, if_false);

                        // Update potential {IfException} uses of {node} to point to the
                        // %ThrowInvalidStringLength runtime call node instead.
                        Node* on_exception = nullptr;
                        if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
                            NodeProperties::ReplaceControlInput(on_exception, vfalse);
                            NodeProperties::ReplaceEffectInput(on_exception, efalse);
                            if_false = graph()->NewNode(common()->IfSuccess(), vfalse);
                            Revisit(on_exception);
                        }

                        // The above %ThrowInvalidStringLength runtime call is an unconditional
                        // throw, making it impossible to return a successful completion in this
                        // case. We simply connect the successful completion to the graph end.
                        if_false = graph()->NewNode(common()->Throw(), efalse, if_false);
                        // TODO(bmeurer): This should be on the AdvancedReducer somehow.
                        NodeProperties::MergeControlToEnd(graph(), common(), if_false);
                        Revisit(graph()->end());
                    }
                    control = graph()->NewNode(common()->IfTrue(), branch);
                    length = effect = graph()->NewNode(common()->TypeGuard(type_cache_->kStringLengthType),
                        length, effect, control);
                }

                // TODO(bmeurer): Ideally this should always use StringConcat and decide to
                // optimize to NewConsString later during SimplifiedLowering, but for that
                // to work we need to know that it's safe to create a ConsString.
                Operator const* const op = r.ShouldCreateConsString()
                    ? simplified()->NewConsString()
                    : simplified()->StringConcat();
                Node* value = graph()->NewNode(op, length, r.left(), r.right());
                ReplaceWithValue(node, value, effect, control);
                return Replace(value);
            }

            // We never get here when we had String feedback.
            DCHECK_NE(BinaryOperationHint::kString, BinaryOperationHintOf(node->op()));
            if (r.OneInputIs(Type::String())) {
                StringAddFlags flags = STRING_ADD_CHECK_NONE;
                if (!r.LeftInputIs(Type::String())) {
                    flags = STRING_ADD_CONVERT_LEFT;
                } else if (!r.RightInputIs(Type::String())) {
                    flags = STRING_ADD_CONVERT_RIGHT;
                }
                Operator::Properties properties = node->op()->properties();
                if (r.NeitherInputCanBe(Type::Receiver())) {
                    // Both sides are already strings, so we know that the
                    // string addition will not cause any observable side
                    // effects; it can still throw obviously.
                    properties = Operator::kNoWrite | Operator::kNoDeopt;
                }

                // JSAdd(x:string, y) => CallStub[StringAdd](x, y)
                // JSAdd(x, y:string) => CallStub[StringAdd](x, y)
                Callable const callable = CodeFactory::StringAdd(isolate(), flags);
                auto call_descriptor = Linkage::GetStubCallDescriptor(
                    graph()->zone(), callable.descriptor(),
                    callable.descriptor().GetStackParameterCount(),
                    CallDescriptor::kNeedsFrameState, properties);
                DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(node->op()));
                node->InsertInput(graph()->zone(), 0,
                    jsgraph()->HeapConstant(callable.code()));
                NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
                return Changed(node);
            }
            return NoChange();
        }

        Reduction JSTypedLowering::ReduceNumberBinop(Node* node)
        {
            JSBinopReduction r(this, node);
            if (r.BothInputsAre(Type::PlainPrimitive())) {
                r.ConvertInputsToNumber();
                return r.ChangeToPureOperator(r.NumberOp(), Type::Number());
            }
            return NoChange();
        }

        Reduction JSTypedLowering::ReduceInt32Binop(Node* node)
        {
            JSBinopReduction r(this, node);
            if (r.BothInputsAre(Type::PlainPrimitive())) {
                r.ConvertInputsToNumber();
                r.ConvertInputsToUI32(kSigned, kSigned);
                return r.ChangeToPureOperator(r.NumberOp(), Type::Signed32());
            }
            return NoChange();
        }

        Reduction JSTypedLowering::ReduceUI32Shift(Node* node, Signedness signedness)
        {
            JSBinopReduction r(this, node);
            if (r.BothInputsAre(Type::PlainPrimitive())) {
                r.ConvertInputsToNumber();
                r.ConvertInputsToUI32(signedness, kUnsigned);
                return r.ChangeToPureOperator(r.NumberOp(), signedness == kUnsigned ? Type::Unsigned32() : Type::Signed32());
            }
            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSComparison(Node* node)
        {
            JSBinopReduction r(this, node);
            if (r.BothInputsAre(Type::String())) {
                // If both inputs are definitely strings, perform a string comparison.
                const Operator* stringOp;
                switch (node->opcode()) {
                case IrOpcode::kJSLessThan:
                    stringOp = simplified()->StringLessThan();
                    break;
                case IrOpcode::kJSGreaterThan:
                    stringOp = simplified()->StringLessThan();
                    r.SwapInputs(); // a > b => b < a
                    break;
                case IrOpcode::kJSLessThanOrEqual:
                    stringOp = simplified()->StringLessThanOrEqual();
                    break;
                case IrOpcode::kJSGreaterThanOrEqual:
                    stringOp = simplified()->StringLessThanOrEqual();
                    r.SwapInputs(); // a >= b => b <= a
                    break;
                default:
                    return NoChange();
                }
                r.ChangeToPureOperator(stringOp);
                return Changed(node);
            }

            const Operator* less_than;
            const Operator* less_than_or_equal;
            if (r.BothInputsAre(Type::Signed32()) || r.BothInputsAre(Type::Unsigned32())) {
                less_than = simplified()->NumberLessThan();
                less_than_or_equal = simplified()->NumberLessThanOrEqual();
            } else if (r.OneInputCannotBe(Type::StringOrReceiver()) && r.BothInputsAre(Type::PlainPrimitive())) {
                r.ConvertInputsToNumber();
                less_than = simplified()->NumberLessThan();
                less_than_or_equal = simplified()->NumberLessThanOrEqual();
            } else if (r.IsStringCompareOperation()) {
                r.CheckInputsToString();
                less_than = simplified()->StringLessThan();
                less_than_or_equal = simplified()->StringLessThanOrEqual();
            } else {
                return NoChange();
            }
            const Operator* comparison;
            switch (node->opcode()) {
            case IrOpcode::kJSLessThan:
                comparison = less_than;
                break;
            case IrOpcode::kJSGreaterThan:
                comparison = less_than;
                r.SwapInputs(); // a > b => b < a
                break;
            case IrOpcode::kJSLessThanOrEqual:
                comparison = less_than_or_equal;
                break;
            case IrOpcode::kJSGreaterThanOrEqual:
                comparison = less_than_or_equal;
                r.SwapInputs(); // a >= b => b <= a
                break;
            default:
                return NoChange();
            }
            return r.ChangeToPureOperator(comparison);
        }

        Reduction JSTypedLowering::ReduceJSEqual(Node* node)
        {
            JSBinopReduction r(this, node);

            if (r.BothInputsAre(Type::UniqueName())) {
                return r.ChangeToPureOperator(simplified()->ReferenceEqual());
            }
            if (r.IsInternalizedStringCompareOperation()) {
                r.CheckInputsToInternalizedString();
                return r.ChangeToPureOperator(simplified()->ReferenceEqual());
            }
            if (r.BothInputsAre(Type::String())) {
                return r.ChangeToPureOperator(simplified()->StringEqual());
            }
            if (r.BothInputsAre(Type::Boolean())) {
                return r.ChangeToPureOperator(simplified()->ReferenceEqual());
            }
            if (r.BothInputsAre(Type::Receiver())) {
                return r.ChangeToPureOperator(simplified()->ReferenceEqual());
            }
            if (r.OneInputIs(Type::Undetectable())) {
                RelaxEffectsAndControls(node);
                node->RemoveInput(r.LeftInputIs(Type::Undetectable()) ? 0 : 1);
                node->TrimInputCount(1);
                NodeProperties::ChangeOp(node, simplified()->ObjectIsUndetectable());
                return Changed(node);
            }

            if (r.BothInputsAre(Type::Signed32()) || r.BothInputsAre(Type::Unsigned32())) {
                return r.ChangeToPureOperator(simplified()->NumberEqual());
            } else if (r.BothInputsAre(Type::Number())) {
                return r.ChangeToPureOperator(simplified()->NumberEqual());
            } else if (r.IsReceiverCompareOperation()) {
                r.CheckInputsToReceiver();
                return r.ChangeToPureOperator(simplified()->ReferenceEqual());
            } else if (r.IsReceiverOrNullOrUndefinedCompareOperation()) {
                // Check that both inputs are Receiver, Null or Undefined.
                r.CheckInputsToReceiverOrNullOrUndefined();

                // If one side is known to be a detectable receiver now, we
                // can simply perform reference equality here, since this
                // known detectable receiver is going to only match itself.
                if (r.OneInputIs(Type::DetectableReceiver())) {
                    return r.ChangeToPureOperator(simplified()->ReferenceEqual());
                }

                // Known that both sides are Receiver, Null or Undefined, the
                // abstract equality operation can be performed like this:
                //
                //   if ObjectIsUndetectable(left)
                //     then ObjectIsUndetectable(right)
                //     else ReferenceEqual(left, right)
                //
                Node* left = r.left();
                Node* right = r.right();
                Node* effect = r.effect();
                Node* control = r.control();

                Node* check = graph()->NewNode(simplified()->ObjectIsUndetectable(), left);
                Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);

                Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
                Node* vtrue = graph()->NewNode(simplified()->ObjectIsUndetectable(), right);

                Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
                Node* vfalse = graph()->NewNode(simplified()->ReferenceEqual(), left, right);

                control = graph()->NewNode(common()->Merge(2), if_true, if_false);
                Node* value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                    vtrue, vfalse, control);
                ReplaceWithValue(node, value, effect, control);
                return Replace(value);
            } else if (r.IsStringCompareOperation()) {
                r.CheckInputsToString();
                return r.ChangeToPureOperator(simplified()->StringEqual());
            } else if (r.IsSymbolCompareOperation()) {
                r.CheckInputsToSymbol();
                return r.ChangeToPureOperator(simplified()->ReferenceEqual());
            }
            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node)
        {
            JSBinopReduction r(this, node);
            if (r.left() == r.right()) {
                // x === x is always true if x != NaN
                Node* replacement = graph()->NewNode(
                    simplified()->BooleanNot(),
                    graph()->NewNode(simplified()->ObjectIsNaN(), r.left()));
                ReplaceWithValue(node, replacement);
                return Replace(replacement);
            }
            if (r.OneInputCannotBe(Type::NumericOrString())) {
                // For values with canonical representation (i.e. neither String nor
                // Numeric) an empty type intersection means the values cannot be strictly
                // equal.
                if (!r.left_type().Maybe(r.right_type())) {
                    Node* replacement = jsgraph()->FalseConstant();
                    ReplaceWithValue(node, replacement);
                    return Replace(replacement);
                }
            }

            if (r.BothInputsAre(Type::Unique())) {
                return r.ChangeToPureOperator(simplified()->ReferenceEqual());
            }
            if (r.OneInputIs(pointer_comparable_type_)) {
                return r.ChangeToPureOperator(simplified()->ReferenceEqual());
            }
            if (r.IsInternalizedStringCompareOperation()) {
                r.CheckInputsToInternalizedString();
                return r.ChangeToPureOperator(simplified()->ReferenceEqual());
            }
            if (r.BothInputsAre(Type::String())) {
                return r.ChangeToPureOperator(simplified()->StringEqual());
            }

            NumberOperationHint hint;
            if (r.BothInputsAre(Type::Signed32()) || r.BothInputsAre(Type::Unsigned32())) {
                return r.ChangeToPureOperator(simplified()->NumberEqual());
            } else if (r.GetCompareNumberOperationHint(&hint)) {
                return r.ChangeToSpeculativeOperator(
                    simplified()->SpeculativeNumberEqual(hint), Type::Boolean());
            } else if (r.BothInputsAre(Type::Number())) {
                return r.ChangeToPureOperator(simplified()->NumberEqual());
            } else if (r.IsReceiverCompareOperation()) {
                // For strict equality, it's enough to know that one input is a Receiver,
                // as a strict equality comparison with a Receiver can only yield true if
                // both sides refer to the same Receiver.
                r.CheckLeftInputToReceiver();
                return r.ChangeToPureOperator(simplified()->ReferenceEqual());
            } else if (r.IsReceiverOrNullOrUndefinedCompareOperation()) {
                // For strict equality, it's enough to know that one input is a Receiver,
                // Null or Undefined, as a strict equality comparison with a Receiver,
                // Null or Undefined can only yield true if both sides refer to the same
                // instance.
                r.CheckLeftInputToReceiverOrNullOrUndefined();
                return r.ChangeToPureOperator(simplified()->ReferenceEqual());
            } else if (r.IsStringCompareOperation()) {
                r.CheckInputsToString();
                return r.ChangeToPureOperator(simplified()->StringEqual());
            } else if (r.IsSymbolCompareOperation()) {
                // For strict equality, it's enough to know that one input is a Symbol,
                // as a strict equality comparison with a Symbol can only yield true if
                // both sides refer to the same Symbol.
                r.CheckLeftInputToSymbol();
                return r.ChangeToPureOperator(simplified()->ReferenceEqual());
            }
            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSToName(Node* node)
        {
            Node* const input = NodeProperties::GetValueInput(node, 0);
            Type const input_type = NodeProperties::GetType(input);
            if (input_type.Is(Type::Name())) {
                // JSToName(x:name) => x
                ReplaceWithValue(node, input);
                return Replace(input);
            }
            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSToLength(Node* node)
        {
            Node* input = NodeProperties::GetValueInput(node, 0);
            Type input_type = NodeProperties::GetType(input);
            if (input_type.Is(type_cache_->kIntegerOrMinusZero)) {
                if (input_type.IsNone() || input_type.Max() <= 0.0) {
                    input = jsgraph()->ZeroConstant();
                } else if (input_type.Min() >= kMaxSafeInteger) {
                    input = jsgraph()->Constant(kMaxSafeInteger);
                } else {
                    if (input_type.Min() <= 0.0) {
                        input = graph()->NewNode(simplified()->NumberMax(),
                            jsgraph()->ZeroConstant(), input);
                    }
                    if (input_type.Max() > kMaxSafeInteger) {
                        input = graph()->NewNode(simplified()->NumberMin(),
                            jsgraph()->Constant(kMaxSafeInteger), input);
                    }
                }
                ReplaceWithValue(node, input);
                return Replace(input);
            }
            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSToNumberInput(Node* input)
        {
            // Try constant-folding of JSToNumber with constant inputs.
            Type input_type = NodeProperties::GetType(input);

            if (input_type.Is(Type::String())) {
                HeapObjectMatcher m(input);
                if (m.HasValue() && m.Ref(broker()).IsString()) {
                    StringRef input_value = m.Ref(broker()).AsString();
                    double number;
                    ASSIGN_RETURN_NO_CHANGE_IF_DATA_MISSING(number, input_value.ToNumber());
                    return Replace(jsgraph()->Constant(number));
                }
            }
            if (input_type.IsHeapConstant()) {
                HeapObjectRef input_value = input_type.AsHeapConstant()->Ref();
                double value;
                if (input_value.OddballToNumber().To(&value)) {
                    return Replace(jsgraph()->Constant(value));
                }
            }
            if (input_type.Is(Type::Number())) {
                // JSToNumber(x:number) => x
                return Changed(input);
            }
            if (input_type.Is(Type::Undefined())) {
                // JSToNumber(undefined) => #NaN
                return Replace(jsgraph()->NaNConstant());
            }
            if (input_type.Is(Type::Null())) {
                // JSToNumber(null) => #0
                return Replace(jsgraph()->ZeroConstant());
            }
            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSToNumber(Node* node)
        {
            // Try to reduce the input first.
            Node* const input = node->InputAt(0);
            Reduction reduction = ReduceJSToNumberInput(input);
            if (reduction.Changed()) {
                ReplaceWithValue(node, reduction.replacement());
                return reduction;
            }
            Type const input_type = NodeProperties::GetType(input);
            if (input_type.Is(Type::PlainPrimitive())) {
                RelaxEffectsAndControls(node);
                node->TrimInputCount(1);
                // For a PlainPrimitive, ToNumeric is the same as ToNumber.
                Type node_type = NodeProperties::GetType(node);
                NodeProperties::SetType(
                    node, Type::Intersect(node_type, Type::Number(), graph()->zone()));
                NodeProperties::ChangeOp(node, simplified()->PlainPrimitiveToNumber());
                return Changed(node);
            }
            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSToNumeric(Node* node)
        {
            Node* const input = NodeProperties::GetValueInput(node, 0);
            Type const input_type = NodeProperties::GetType(input);
            if (input_type.Is(Type::NonBigIntPrimitive())) {
                // ToNumeric(x:primitive\bigint) => ToNumber(x)
                NodeProperties::ChangeOp(node, javascript()->ToNumber());
                Reduction const reduction = ReduceJSToNumber(node);
                return reduction.Changed() ? reduction : Changed(node);
            }
            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSToStringInput(Node* input)
        {
            if (input->opcode() == IrOpcode::kJSToString) {
                // Recursively try to reduce the input first.
                Reduction result = ReduceJSToString(input);
                if (result.Changed())
                    return result;
                return Changed(input); // JSToString(JSToString(x)) => JSToString(x)
            }
            Type input_type = NodeProperties::GetType(input);
            if (input_type.Is(Type::String())) {
                return Changed(input); // JSToString(x:string) => x
            }
            if (input_type.Is(Type::Boolean())) {
                return Replace(graph()->NewNode(
                    common()->Select(MachineRepresentation::kTagged), input,
                    jsgraph()->HeapConstant(factory()->true_string()),
                    jsgraph()->HeapConstant(factory()->false_string())));
            }
            if (input_type.Is(Type::Undefined())) {
                return Replace(jsgraph()->HeapConstant(factory()->undefined_string()));
            }
            if (input_type.Is(Type::Null())) {
                return Replace(jsgraph()->HeapConstant(factory()->null_string()));
            }
            if (input_type.Is(Type::NaN())) {
                return Replace(jsgraph()->HeapConstant(factory()->NaN_string()));
            }
            if (input_type.Is(Type::Number())) {
                return Replace(graph()->NewNode(simplified()->NumberToString(), input));
            }
            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSToString(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSToString, node->opcode());
            // Try to reduce the input first.
            Node* const input = node->InputAt(0);
            Reduction reduction = ReduceJSToStringInput(input);
            if (reduction.Changed()) {
                ReplaceWithValue(node, reduction.replacement());
                return reduction;
            }
            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSToObject(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSToObject, node->opcode());
            Node* receiver = NodeProperties::GetValueInput(node, 0);
            Type receiver_type = NodeProperties::GetType(receiver);
            Node* context = NodeProperties::GetContextInput(node);
            Node* frame_state = NodeProperties::GetFrameStateInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            if (receiver_type.Is(Type::Receiver())) {
                ReplaceWithValue(node, receiver, effect, control);
                return Replace(receiver);
            }

            // Check whether {receiver} is a spec object.
            Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), receiver);
            Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);

            Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
            Node* etrue = effect;
            Node* rtrue = receiver;

            Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
            Node* efalse = effect;
            Node* rfalse;
            {
                // Convert {receiver} using the ToObjectStub.
                Callable callable = Builtins::CallableFor(isolate(), Builtins::kToObject);
                auto call_descriptor = Linkage::GetStubCallDescriptor(
                    graph()->zone(), callable.descriptor(),
                    callable.descriptor().GetStackParameterCount(),
                    CallDescriptor::kNeedsFrameState, node->op()->properties());
                rfalse = efalse = if_false = graph()->NewNode(common()->Call(call_descriptor),
                    jsgraph()->HeapConstant(callable.code()), receiver,
                    context, frame_state, efalse, if_false);
            }

            // Update potential {IfException} uses of {node} to point to the above
            // ToObject stub call node instead. Note that the stub can only throw on
            // receivers that can be null or undefined.
            Node* on_exception = nullptr;
            if (receiver_type.Maybe(Type::NullOrUndefined()) && NodeProperties::IsExceptionalCall(node, &on_exception)) {
                NodeProperties::ReplaceControlInput(on_exception, if_false);
                NodeProperties::ReplaceEffectInput(on_exception, efalse);
                if_false = graph()->NewNode(common()->IfSuccess(), if_false);
                Revisit(on_exception);
            }

            control = graph()->NewNode(common()->Merge(2), if_true, if_false);
            effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);

            // Morph the {node} into an appropriate Phi.
            ReplaceWithValue(node, node, effect, control);
            node->ReplaceInput(0, rtrue);
            node->ReplaceInput(1, rfalse);
            node->ReplaceInput(2, control);
            node->TrimInputCount(3);
            NodeProperties::ChangeOp(node,
                common()->Phi(MachineRepresentation::kTagged, 2));
            return Changed(node);
        }

        Reduction JSTypedLowering::ReduceJSLoadNamed(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode());
            Node* receiver = NodeProperties::GetValueInput(node, 0);
            Type receiver_type = NodeProperties::GetType(receiver);
            NameRef name(broker(), NamedAccessOf(node->op()).name());
            NameRef length_str(broker(), factory()->length_string());
            // Optimize "length" property of strings.
            if (name.equals(length_str) && receiver_type.Is(Type::String())) {
                Node* value = graph()->NewNode(simplified()->StringLength(), receiver);
                ReplaceWithValue(node, value);
                return Replace(value);
            }
            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSHasInPrototypeChain(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSHasInPrototypeChain, node->opcode());
            Node* value = NodeProperties::GetValueInput(node, 0);
            Type value_type = NodeProperties::GetType(value);
            Node* prototype = NodeProperties::GetValueInput(node, 1);
            Node* context = NodeProperties::GetContextInput(node);
            Node* frame_state = NodeProperties::GetFrameStateInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // If {value} cannot be a receiver, then it cannot have {prototype} in
            // it's prototype chain (all Primitive values have a null prototype).
            if (value_type.Is(Type::Primitive())) {
                Node* value = jsgraph()->FalseConstant();
                ReplaceWithValue(node, value, effect, control);
                return Replace(value);
            }

            Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), value);
            Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control);

            Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
            Node* etrue0 = effect;
            Node* vtrue0 = jsgraph()->FalseConstant();

            control = graph()->NewNode(common()->IfFalse(), branch0);

            // Loop through the {value}s prototype chain looking for the {prototype}.
            Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
            Node* eloop = effect = graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
            Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
            NodeProperties::MergeControlToEnd(graph(), common(), terminate);
            Node* vloop = value = graph()->NewNode(
                common()->Phi(MachineRepresentation::kTagged, 2), value, value, loop);
            NodeProperties::SetType(vloop, Type::NonInternal());

            // Load the {value} map and instance type.
            Node* value_map = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForMap()), value, effect, control);
            Node* value_instance_type = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForMapInstanceType()), value_map,
                effect, control);

            // Check if the {value} is a special receiver, because for special
            // receivers, i.e. proxies or API values that need access checks,
            // we have to use the %HasInPrototypeChain runtime function instead.
            Node* check1 = graph()->NewNode(
                simplified()->NumberLessThanOrEqual(), value_instance_type,
                jsgraph()->Constant(LAST_SPECIAL_RECEIVER_TYPE));
            Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse), check1, control);

            control = graph()->NewNode(common()->IfFalse(), branch1);

            Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
            Node* etrue1 = effect;
            Node* vtrue1;

            // Check if the {value} is not a receiver at all.
            Node* check10 = graph()->NewNode(simplified()->NumberLessThan(), value_instance_type,
                jsgraph()->Constant(FIRST_JS_RECEIVER_TYPE));
            Node* branch10 = graph()->NewNode(common()->Branch(BranchHint::kTrue), check10, if_true1);

            // A primitive value cannot match the {prototype} we're looking for.
            if_true1 = graph()->NewNode(common()->IfTrue(), branch10);
            vtrue1 = jsgraph()->FalseConstant();

            Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch10);
            Node* efalse1 = etrue1;
            Node* vfalse1;
            {
                // Slow path, need to call the %HasInPrototypeChain runtime function.
                vfalse1 = efalse1 = if_false1 = graph()->NewNode(
                    javascript()->CallRuntime(Runtime::kHasInPrototypeChain), value,
                    prototype, context, frame_state, efalse1, if_false1);

                // Replace any potential {IfException} uses of {node} to catch
                // exceptions from this %HasInPrototypeChain runtime call instead.
                Node* on_exception = nullptr;
                if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
                    NodeProperties::ReplaceControlInput(on_exception, vfalse1);
                    NodeProperties::ReplaceEffectInput(on_exception, efalse1);
                    if_false1 = graph()->NewNode(common()->IfSuccess(), vfalse1);
                    Revisit(on_exception);
                }
            }

            // Load the {value} prototype.
            Node* value_prototype = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForMapPrototype()), value_map,
                effect, control);

            // Check if we reached the end of {value}s prototype chain.
            Node* check2 = graph()->NewNode(simplified()->ReferenceEqual(),
                value_prototype, jsgraph()->NullConstant());
            Node* branch2 = graph()->NewNode(common()->Branch(), check2, control);

            Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
            Node* etrue2 = effect;
            Node* vtrue2 = jsgraph()->FalseConstant();

            control = graph()->NewNode(common()->IfFalse(), branch2);

            // Check if we reached the {prototype}.
            Node* check3 = graph()->NewNode(simplified()->ReferenceEqual(),
                value_prototype, prototype);
            Node* branch3 = graph()->NewNode(common()->Branch(), check3, control);

            Node* if_true3 = graph()->NewNode(common()->IfTrue(), branch3);
            Node* etrue3 = effect;
            Node* vtrue3 = jsgraph()->TrueConstant();

            control = graph()->NewNode(common()->IfFalse(), branch3);

            // Close the loop.
            vloop->ReplaceInput(1, value_prototype);
            eloop->ReplaceInput(1, effect);
            loop->ReplaceInput(1, control);

            control = graph()->NewNode(common()->Merge(5), if_true0, if_true1, if_true2,
                if_true3, if_false1);
            effect = graph()->NewNode(common()->EffectPhi(5), etrue0, etrue1, etrue2,
                etrue3, efalse1, control);

            // Morph the {node} into an appropriate Phi.
            ReplaceWithValue(node, node, effect, control);
            node->ReplaceInput(0, vtrue0);
            node->ReplaceInput(1, vtrue1);
            node->ReplaceInput(2, vtrue2);
            node->ReplaceInput(3, vtrue3);
            node->ReplaceInput(4, vfalse1);
            node->ReplaceInput(5, control);
            node->TrimInputCount(6);
            NodeProperties::ChangeOp(node,
                common()->Phi(MachineRepresentation::kTagged, 5));
            return Changed(node);
        }

        Reduction JSTypedLowering::ReduceJSOrdinaryHasInstance(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSOrdinaryHasInstance, node->opcode());
            Node* constructor = NodeProperties::GetValueInput(node, 0);
            Type constructor_type = NodeProperties::GetType(constructor);
            Node* object = NodeProperties::GetValueInput(node, 1);
            Type object_type = NodeProperties::GetType(object);

            // Check if the {constructor} cannot be callable.
            // See ES6 section 7.3.19 OrdinaryHasInstance ( C, O ) step 1.
            if (!constructor_type.Maybe(Type::Callable())) {
                Node* value = jsgraph()->FalseConstant();
                ReplaceWithValue(node, value);
                return Replace(value);
            }

            // If the {constructor} cannot be a JSBoundFunction and then {object}
            // cannot be a JSReceiver, then this can be constant-folded to false.
            // See ES6 section 7.3.19 OrdinaryHasInstance ( C, O ) step 2 and 3.
            if (!object_type.Maybe(Type::Receiver()) && !constructor_type.Maybe(Type::BoundFunction())) {
                Node* value = jsgraph()->FalseConstant();
                ReplaceWithValue(node, value);
                return Replace(value);
            }

            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSLoadContext(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
            ContextAccess const& access = ContextAccessOf(node->op());
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* context = NodeProperties::GetContextInput(node);
            Node* control = graph()->start();
            for (size_t i = 0; i < access.depth(); ++i) {
                context = effect = graph()->NewNode(
                    simplified()->LoadField(
                        AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX)),
                    context, effect, control);
            }
            node->ReplaceInput(0, context);
            node->ReplaceInput(1, effect);
            node->AppendInput(jsgraph()->zone(), control);
            NodeProperties::ChangeOp(
                node,
                simplified()->LoadField(AccessBuilder::ForContextSlot(access.index())));
            return Changed(node);
        }

        Reduction JSTypedLowering::ReduceJSStoreContext(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
            ContextAccess const& access = ContextAccessOf(node->op());
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* context = NodeProperties::GetContextInput(node);
            Node* control = graph()->start();
            Node* value = NodeProperties::GetValueInput(node, 0);
            for (size_t i = 0; i < access.depth(); ++i) {
                context = effect = graph()->NewNode(
                    simplified()->LoadField(
                        AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX)),
                    context, effect, control);
            }
            node->ReplaceInput(0, context);
            node->ReplaceInput(1, value);
            node->ReplaceInput(2, effect);
            NodeProperties::ChangeOp(
                node,
                simplified()->StoreField(AccessBuilder::ForContextSlot(access.index())));
            return Changed(node);
        }

        Node* JSTypedLowering::BuildGetModuleCell(Node* node)
        {
            DCHECK(node->opcode() == IrOpcode::kJSLoadModule || node->opcode() == IrOpcode::kJSStoreModule);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            int32_t cell_index = OpParameter<int32_t>(node->op());
            Node* module = NodeProperties::GetValueInput(node, 0);
            Type module_type = NodeProperties::GetType(module);

            if (module_type.IsHeapConstant()) {
                ModuleRef module_constant = module_type.AsHeapConstant()->Ref().AsModule();
                CellRef cell_constant = module_constant.GetCell(cell_index);
                return jsgraph()->Constant(cell_constant);
            }

            FieldAccess field_access;
            int index;
            if (ModuleDescriptor::GetCellIndexKind(cell_index) == ModuleDescriptor::kExport) {
                field_access = AccessBuilder::ForModuleRegularExports();
                index = cell_index - 1;
            } else {
                DCHECK_EQ(ModuleDescriptor::GetCellIndexKind(cell_index),
                    ModuleDescriptor::kImport);
                field_access = AccessBuilder::ForModuleRegularImports();
                index = -cell_index - 1;
            }
            Node* array = effect = graph()->NewNode(simplified()->LoadField(field_access),
                module, effect, control);
            return graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForFixedArraySlot(index)), array,
                effect, control);
        }

        Reduction JSTypedLowering::ReduceJSLoadModule(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSLoadModule, node->opcode());
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            Node* cell = BuildGetModuleCell(node);
            if (cell->op()->EffectOutputCount() > 0)
                effect = cell;
            Node* value = effect = graph()->NewNode(simplified()->LoadField(AccessBuilder::ForCellValue()),
                cell, effect, control);

            ReplaceWithValue(node, value, effect, control);
            return Changed(value);
        }

        Reduction JSTypedLowering::ReduceJSStoreModule(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSStoreModule, node->opcode());
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* value = NodeProperties::GetValueInput(node, 1);
            DCHECK_EQ(
                ModuleDescriptor::GetCellIndexKind(OpParameter<int32_t>(node->op())),
                ModuleDescriptor::kExport);

            Node* cell = BuildGetModuleCell(node);
            if (cell->op()->EffectOutputCount() > 0)
                effect = cell;
            effect = graph()->NewNode(simplified()->StoreField(AccessBuilder::ForCellValue()),
                cell, value, effect, control);

            ReplaceWithValue(node, effect, effect, control);
            return Changed(value);
        }

        namespace {

            void ReduceBuiltin(JSGraph* jsgraph, Node* node, int builtin_index, int arity,
                CallDescriptor::Flags flags)
            {
                // Patch {node} to a direct CEntry call.
                //
                // ----------- A r g u m e n t s -----------
                // -- 0: CEntry
                // --- Stack args ---
                // -- 1: receiver
                // -- [2, 2 + n[: the n actual arguments passed to the builtin
                // -- 2 + n: argc, including the receiver and implicit args (Smi)
                // -- 2 + n + 1: target
                // -- 2 + n + 2: new_target
                // --- Register args ---
                // -- 2 + n + 3: the C entry point
                // -- 2 + n + 4: argc (Int32)
                // -----------------------------------

                // The logic contained here is mirrored in Builtins::Generate_Adaptor.
                // Keep these in sync.

                const bool is_construct = (node->opcode() == IrOpcode::kJSConstruct);

                DCHECK(Builtins::HasCppImplementation(builtin_index));

                Node* target = NodeProperties::GetValueInput(node, 0);
                Node* new_target = is_construct
                    ? NodeProperties::GetValueInput(node, arity + 1)
                    : jsgraph->UndefinedConstant();

                // API and CPP builtins are implemented in C++, and we can inline both.
                // CPP builtins create a builtin exit frame, API builtins don't.
                const bool has_builtin_exit_frame = Builtins::IsCpp(builtin_index);

                Node* stub = jsgraph->CEntryStubConstant(1, kDontSaveFPRegs, kArgvOnStack,
                    has_builtin_exit_frame);
                node->ReplaceInput(0, stub);

                Zone* zone = jsgraph->zone();
                if (is_construct) {
                    // Unify representations between construct and call nodes.
                    // Remove new target and add receiver as a stack parameter.
                    Node* receiver = jsgraph->UndefinedConstant();
                    node->RemoveInput(arity + 1);
                    node->InsertInput(zone, 1, receiver);
                }

                const int argc = arity + BuiltinArguments::kNumExtraArgsWithReceiver;
                Node* argc_node = jsgraph->Constant(argc);

                static const int kStubAndReceiver = 2;
                int cursor = arity + kStubAndReceiver;
                node->InsertInput(zone, cursor++, jsgraph->PaddingConstant());
                node->InsertInput(zone, cursor++, argc_node);
                node->InsertInput(zone, cursor++, target);
                node->InsertInput(zone, cursor++, new_target);

                Address entry = Builtins::CppEntryOf(builtin_index);
                ExternalReference entry_ref = ExternalReference::Create(entry);
                Node* entry_node = jsgraph->ExternalConstant(entry_ref);

                node->InsertInput(zone, cursor++, entry_node);
                node->InsertInput(zone, cursor++, argc_node);

                static const int kReturnCount = 1;
                const char* debug_name = Builtins::name(builtin_index);
                Operator::Properties properties = node->op()->properties();
                auto call_descriptor = Linkage::GetCEntryStubCallDescriptor(
                    zone, kReturnCount, argc, debug_name, properties, flags);

                NodeProperties::ChangeOp(node, jsgraph->common()->Call(call_descriptor));
            }

            bool NeedsArgumentAdaptorFrame(SharedFunctionInfoRef shared, int arity)
            {
                static const int sentinel = SharedFunctionInfo::kDontAdaptArgumentsSentinel;
                const int num_decl_parms = shared.internal_formal_parameter_count();
                return (num_decl_parms != arity && num_decl_parms != sentinel);
            }

        } // namespace

        Reduction JSTypedLowering::ReduceJSConstructForwardVarargs(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSConstructForwardVarargs, node->opcode());
            ConstructForwardVarargsParameters p = ConstructForwardVarargsParametersOf(node->op());
            DCHECK_LE(2u, p.arity());
            int const arity = static_cast<int>(p.arity() - 2);
            int const start_index = static_cast<int>(p.start_index());
            Node* target = NodeProperties::GetValueInput(node, 0);
            Type target_type = NodeProperties::GetType(target);
            Node* new_target = NodeProperties::GetValueInput(node, arity + 1);

            // Check if {target} is a JSFunction.
            if (target_type.IsHeapConstant() && target_type.AsHeapConstant()->Ref().IsJSFunction()) {
                // Only optimize [[Construct]] here if {function} is a Constructor.
                JSFunctionRef function = target_type.AsHeapConstant()->Ref().AsJSFunction();
                if (!function.map().is_constructor())
                    return NoChange();
                // Patch {node} to an indirect call via ConstructFunctionForwardVarargs.
                Callable callable = CodeFactory::ConstructFunctionForwardVarargs(isolate());
                node->RemoveInput(arity + 1);
                node->InsertInput(graph()->zone(), 0,
                    jsgraph()->HeapConstant(callable.code()));
                node->InsertInput(graph()->zone(), 2, new_target);
                node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(arity));
                node->InsertInput(graph()->zone(), 4, jsgraph()->Constant(start_index));
                node->InsertInput(graph()->zone(), 5, jsgraph()->UndefinedConstant());
                NodeProperties::ChangeOp(
                    node, common()->Call(Linkage::GetStubCallDescriptor(graph()->zone(), callable.descriptor(), arity + 1, CallDescriptor::kNeedsFrameState)));
                return Changed(node);
            }

            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSConstruct(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode());
            ConstructParameters const& p = ConstructParametersOf(node->op());
            DCHECK_LE(2u, p.arity());
            int const arity = static_cast<int>(p.arity() - 2);
            Node* target = NodeProperties::GetValueInput(node, 0);
            Type target_type = NodeProperties::GetType(target);
            Node* new_target = NodeProperties::GetValueInput(node, arity + 1);

            // Check if {target} is a known JSFunction.
            if (target_type.IsHeapConstant() && target_type.AsHeapConstant()->Ref().IsJSFunction()) {
                JSFunctionRef function = target_type.AsHeapConstant()->Ref().AsJSFunction();
                SharedFunctionInfoRef shared = function.shared();

                // Only optimize [[Construct]] here if {function} is a Constructor.
                if (!function.map().is_constructor())
                    return NoChange();

                CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;

                // Patch {node} to an indirect call via the {function}s construct stub.
                bool use_builtin_construct_stub = shared.construct_as_builtin();

                CodeRef code(broker(),
                    use_builtin_construct_stub
                        ? BUILTIN_CODE(isolate(), JSBuiltinsConstructStub)
                        : BUILTIN_CODE(isolate(), JSConstructStubGeneric));

                node->RemoveInput(arity + 1);
                node->InsertInput(graph()->zone(), 0, jsgraph()->Constant(code));
                node->InsertInput(graph()->zone(), 2, new_target);
                node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(arity));
                node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant());
                node->InsertInput(graph()->zone(), 5, jsgraph()->UndefinedConstant());
                NodeProperties::ChangeOp(
                    node,
                    common()->Call(Linkage::GetStubCallDescriptor(
                        graph()->zone(), ConstructStubDescriptor {}, 1 + arity, flags)));

                return Changed(node);
            }

            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSCallForwardVarargs(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCallForwardVarargs, node->opcode());
            CallForwardVarargsParameters p = CallForwardVarargsParametersOf(node->op());
            DCHECK_LE(2u, p.arity());
            int const arity = static_cast<int>(p.arity() - 2);
            int const start_index = static_cast<int>(p.start_index());
            Node* target = NodeProperties::GetValueInput(node, 0);
            Type target_type = NodeProperties::GetType(target);

            // Check if {target} is a JSFunction.
            if (target_type.Is(Type::Function())) {
                // Compute flags for the call.
                CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
                // Patch {node} to an indirect call via CallFunctionForwardVarargs.
                Callable callable = CodeFactory::CallFunctionForwardVarargs(isolate());
                node->InsertInput(graph()->zone(), 0,
                    jsgraph()->HeapConstant(callable.code()));
                node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(arity));
                node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(start_index));
                NodeProperties::ChangeOp(
                    node, common()->Call(Linkage::GetStubCallDescriptor(graph()->zone(), callable.descriptor(), arity + 1, flags)));
                return Changed(node);
            }

            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSCall(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
            CallParameters const& p = CallParametersOf(node->op());
            int arity = static_cast<int>(p.arity() - 2);
            ConvertReceiverMode convert_mode = p.convert_mode();
            Node* target = NodeProperties::GetValueInput(node, 0);
            Type target_type = NodeProperties::GetType(target);
            Node* receiver = NodeProperties::GetValueInput(node, 1);
            Type receiver_type = NodeProperties::GetType(receiver);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Try to infer receiver {convert_mode} from {receiver} type.
            if (receiver_type.Is(Type::NullOrUndefined())) {
                convert_mode = ConvertReceiverMode::kNullOrUndefined;
            } else if (!receiver_type.Maybe(Type::NullOrUndefined())) {
                convert_mode = ConvertReceiverMode::kNotNullOrUndefined;
            }

            // Check if {target} is a known JSFunction.
            if (target_type.IsHeapConstant() && target_type.AsHeapConstant()->Ref().IsJSFunction()) {
                JSFunctionRef function = target_type.AsHeapConstant()->Ref().AsJSFunction();
                SharedFunctionInfoRef shared = function.shared();

                if (shared.HasBreakInfo()) {
                    // Do not inline the call if we need to check whether to break at entry.
                    return NoChange();
                }

                // Class constructors are callable, but [[Call]] will raise an exception.
                // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ).
                if (IsClassConstructor(shared.kind()))
                    return NoChange();

                // Check if we need to convert the {receiver}, but bailout if it would
                // require data from a foreign native context.
                if (is_sloppy(shared.language_mode()) && !shared.native() && !receiver_type.Is(Type::Receiver())) {
                    if (!function.native_context().equals(broker()->native_context())) {
                        return NoChange();
                    }
                    Node* global_proxy = jsgraph()->Constant(function.native_context().global_proxy_object());
                    receiver = effect = graph()->NewNode(simplified()->ConvertReceiver(convert_mode),
                        receiver, global_proxy, effect, control);
                    NodeProperties::ReplaceValueInput(node, receiver, 1);
                }

                // Load the context from the {target}.
                Node* context = effect = graph()->NewNode(
                    simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), target,
                    effect, control);
                NodeProperties::ReplaceContextInput(node, context);

                // Update the effect dependency for the {node}.
                NodeProperties::ReplaceEffectInput(node, effect);

                // Compute flags for the call.
                CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
                Node* new_target = jsgraph()->UndefinedConstant();

                if (NeedsArgumentAdaptorFrame(shared, arity)) {
                    // Check if it's safe to skip the arguments adaptor for {shared},
                    // that is whether the target function anyways cannot observe the
                    // actual arguments. Details can be found in this document at
                    // https://bit.ly/v8-faster-calls-with-arguments-mismatch and
                    // on the tracking bug at https://crbug.com/v8/8895
                    if (shared.is_safe_to_skip_arguments_adaptor()) {
                        // Currently we only support skipping arguments adaptor frames
                        // for strict mode functions, since there's Function.arguments
                        // legacy accessor, which is still available in sloppy mode.
                        DCHECK_EQ(LanguageMode::kStrict, shared.language_mode());

                        // Massage the arguments to match the expected number of arguments.
                        int expected_argument_count = shared.internal_formal_parameter_count();
                        for (; arity > expected_argument_count; --arity) {
                            node->RemoveInput(arity + 1);
                        }
                        for (; arity < expected_argument_count; ++arity) {
                            node->InsertInput(graph()->zone(), arity + 2,
                                jsgraph()->UndefinedConstant());
                        }

                        // Patch {node} to a direct call.
                        node->InsertInput(graph()->zone(), arity + 2, new_target);
                        node->InsertInput(graph()->zone(), arity + 3,
                            jsgraph()->Constant(arity));
                        NodeProperties::ChangeOp(node,
                            common()->Call(Linkage::GetJSCallDescriptor(
                                graph()->zone(), false, 1 + arity,
                                flags | CallDescriptor::kCanUseRoots)));
                    } else {
                        // Patch {node} to an indirect call via the ArgumentsAdaptorTrampoline.
                        Callable callable = CodeFactory::ArgumentAdaptor(isolate());
                        node->InsertInput(graph()->zone(), 0,
                            jsgraph()->HeapConstant(callable.code()));
                        node->InsertInput(graph()->zone(), 2, new_target);
                        node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(arity));
                        node->InsertInput(
                            graph()->zone(), 4,
                            jsgraph()->Constant(shared.internal_formal_parameter_count()));
                        NodeProperties::ChangeOp(
                            node,
                            common()->Call(Linkage::GetStubCallDescriptor(
                                graph()->zone(), callable.descriptor(), 1 + arity, flags)));
                    }
                } else if (shared.HasBuiltinId() && Builtins::HasCppImplementation(shared.builtin_id())) {
                    // Patch {node} to a direct CEntry call.
                    ReduceBuiltin(jsgraph(), node, shared.builtin_id(), arity, flags);
                } else if (shared.HasBuiltinId() && Builtins::KindOf(shared.builtin_id()) == Builtins::TFJ) {
                    // Patch {node} to a direct code object call.
                    Callable callable = Builtins::CallableFor(
                        isolate(), static_cast<Builtins::Name>(shared.builtin_id()));
                    CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;

                    const CallInterfaceDescriptor& descriptor = callable.descriptor();
                    auto call_descriptor = Linkage::GetStubCallDescriptor(
                        graph()->zone(), descriptor, 1 + arity, flags);
                    Node* stub_code = jsgraph()->HeapConstant(callable.code());
                    node->InsertInput(graph()->zone(), 0, stub_code); // Code object.
                    node->InsertInput(graph()->zone(), 2, new_target);
                    node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(arity));
                    NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
                } else {
                    // Patch {node} to a direct call.
                    node->InsertInput(graph()->zone(), arity + 2, new_target);
                    node->InsertInput(graph()->zone(), arity + 3, jsgraph()->Constant(arity));
                    NodeProperties::ChangeOp(node,
                        common()->Call(Linkage::GetJSCallDescriptor(
                            graph()->zone(), false, 1 + arity,
                            flags | CallDescriptor::kCanUseRoots)));
                }
                return Changed(node);
            }

            // Check if {target} is a JSFunction.
            if (target_type.Is(Type::Function())) {
                // Compute flags for the call.
                CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
                // Patch {node} to an indirect call via the CallFunction builtin.
                Callable callable = CodeFactory::CallFunction(isolate(), convert_mode);
                node->InsertInput(graph()->zone(), 0,
                    jsgraph()->HeapConstant(callable.code()));
                node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(arity));
                NodeProperties::ChangeOp(
                    node, common()->Call(Linkage::GetStubCallDescriptor(graph()->zone(), callable.descriptor(), 1 + arity, flags)));
                return Changed(node);
            }

            // Maybe we did at least learn something about the {receiver}.
            if (p.convert_mode() != convert_mode) {
                NodeProperties::ChangeOp(
                    node, javascript()->Call(p.arity(), p.frequency(), p.feedback(), convert_mode, p.speculation_mode()));
                return Changed(node);
            }

            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSForInNext(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSForInNext, node->opcode());
            ForInMode const mode = ForInModeOf(node->op());
            Node* receiver = NodeProperties::GetValueInput(node, 0);
            Node* cache_array = NodeProperties::GetValueInput(node, 1);
            Node* cache_type = NodeProperties::GetValueInput(node, 2);
            Node* index = NodeProperties::GetValueInput(node, 3);
            Node* context = NodeProperties::GetContextInput(node);
            Node* frame_state = NodeProperties::GetFrameStateInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Load the map of the {receiver}.
            Node* receiver_map = effect = graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
                receiver, effect, control);

            switch (mode) {
            case ForInMode::kUseEnumCacheKeys:
            case ForInMode::kUseEnumCacheKeysAndIndices: {
                // Ensure that the expected map still matches that of the {receiver}.
                Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
                    receiver_map, cache_type);
                effect = graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kWrongMap),
                    check, effect, control);

                // Since the change to LoadElement() below is effectful, we connect
                // node to all effect uses.
                ReplaceWithValue(node, node, node, control);

                // Morph the {node} into a LoadElement.
                node->ReplaceInput(0, cache_array);
                node->ReplaceInput(1, index);
                node->ReplaceInput(2, effect);
                node->ReplaceInput(3, control);
                node->TrimInputCount(4);
                NodeProperties::ChangeOp(
                    node,
                    simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()));
                NodeProperties::SetType(node, Type::InternalizedString());
                break;
            }
            case ForInMode::kGeneric: {
                // Load the next {key} from the {cache_array}.
                Node* key = effect = graph()->NewNode(
                    simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()),
                    cache_array, index, effect, control);

                // Check if the expected map still matches that of the {receiver}.
                Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
                    receiver_map, cache_type);
                Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);

                Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
                Node* etrue;
                Node* vtrue;
                {
                    // Don't need filtering since expected map still matches that of the
                    // {receiver}.
                    etrue = effect;
                    vtrue = key;
                }

                Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
                Node* efalse;
                Node* vfalse;
                {
                    // Filter the {key} to check if it's still a valid property of the
                    // {receiver} (does the ToName conversion implicitly).
                    Callable const callable = Builtins::CallableFor(isolate(), Builtins::kForInFilter);
                    auto call_descriptor = Linkage::GetStubCallDescriptor(
                        graph()->zone(), callable.descriptor(),
                        callable.descriptor().GetStackParameterCount(),
                        CallDescriptor::kNeedsFrameState);
                    vfalse = efalse = if_false = graph()->NewNode(common()->Call(call_descriptor),
                        jsgraph()->HeapConstant(callable.code()), key,
                        receiver, context, frame_state, effect, if_false);

                    // Update potential {IfException} uses of {node} to point to the above
                    // ForInFilter stub call node instead.
                    Node* if_exception = nullptr;
                    if (NodeProperties::IsExceptionalCall(node, &if_exception)) {
                        if_false = graph()->NewNode(common()->IfSuccess(), vfalse);
                        NodeProperties::ReplaceControlInput(if_exception, vfalse);
                        NodeProperties::ReplaceEffectInput(if_exception, efalse);
                        Revisit(if_exception);
                    }
                }

                control = graph()->NewNode(common()->Merge(2), if_true, if_false);
                effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
                ReplaceWithValue(node, node, effect, control);

                // Morph the {node} into a Phi.
                node->ReplaceInput(0, vtrue);
                node->ReplaceInput(1, vfalse);
                node->ReplaceInput(2, control);
                node->TrimInputCount(3);
                NodeProperties::ChangeOp(
                    node, common()->Phi(MachineRepresentation::kTagged, 2));
            }
            }

            return Changed(node);
        }

        Reduction JSTypedLowering::ReduceJSForInPrepare(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSForInPrepare, node->opcode());
            ForInMode const mode = ForInModeOf(node->op());
            Node* enumerator = NodeProperties::GetValueInput(node, 0);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            Node* cache_type = enumerator;
            Node* cache_array = nullptr;
            Node* cache_length = nullptr;

            switch (mode) {
            case ForInMode::kUseEnumCacheKeys:
            case ForInMode::kUseEnumCacheKeysAndIndices: {
                // Check that the {enumerator} is a Map.
                effect = graph()->NewNode(
                    simplified()->CheckMaps(CheckMapsFlag::kNone,
                        ZoneHandleSet<Map>(factory()->meta_map())),
                    enumerator, effect, control);

                // Load the enum cache from the {enumerator} map.
                Node* descriptor_array = effect = graph()->NewNode(
                    simplified()->LoadField(AccessBuilder::ForMapDescriptors()),
                    enumerator, effect, control);
                Node* enum_cache = effect = graph()->NewNode(
                    simplified()->LoadField(AccessBuilder::ForDescriptorArrayEnumCache()),
                    descriptor_array, effect, control);
                cache_array = effect = graph()->NewNode(
                    simplified()->LoadField(AccessBuilder::ForEnumCacheKeys()),
                    enum_cache, effect, control);

                // Load the enum length of the {enumerator} map.
                Node* bit_field3 = effect = graph()->NewNode(
                    simplified()->LoadField(AccessBuilder::ForMapBitField3()), enumerator,
                    effect, control);
                STATIC_ASSERT(Map::EnumLengthBits::kShift == 0);
                cache_length = graph()->NewNode(simplified()->NumberBitwiseAnd(), bit_field3,
                    jsgraph()->Constant(Map::EnumLengthBits::kMask));
                break;
            }
            case ForInMode::kGeneric: {
                // Check if the {enumerator} is a Map or a FixedArray.
                Node* check = effect = graph()->NewNode(
                    simplified()->CompareMaps(ZoneHandleSet<Map>(factory()->meta_map())),
                    enumerator, effect, control);
                Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);

                Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
                Node* etrue = effect;
                Node* cache_array_true;
                Node* cache_length_true;
                {
                    // Load the enum cache from the {enumerator} map.
                    Node* descriptor_array = etrue = graph()->NewNode(
                        simplified()->LoadField(AccessBuilder::ForMapDescriptors()),
                        enumerator, etrue, if_true);
                    Node* enum_cache = etrue = graph()->NewNode(simplified()->LoadField(
                                                                    AccessBuilder::ForDescriptorArrayEnumCache()),
                        descriptor_array, etrue, if_true);
                    cache_array_true = etrue = graph()->NewNode(
                        simplified()->LoadField(AccessBuilder::ForEnumCacheKeys()),
                        enum_cache, etrue, if_true);

                    // Load the enum length of the {enumerator} map.
                    Node* bit_field3 = etrue = graph()->NewNode(
                        simplified()->LoadField(AccessBuilder::ForMapBitField3()),
                        enumerator, etrue, if_true);
                    STATIC_ASSERT(Map::EnumLengthBits::kShift == 0);
                    cache_length_true = graph()->NewNode(simplified()->NumberBitwiseAnd(), bit_field3,
                        jsgraph()->Constant(Map::EnumLengthBits::kMask));
                }

                Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
                Node* efalse = effect;
                Node* cache_array_false;
                Node* cache_length_false;
                {
                    // The {enumerator} is the FixedArray with the keys to iterate.
                    cache_array_false = enumerator;
                    cache_length_false = efalse = graph()->NewNode(
                        simplified()->LoadField(AccessBuilder::ForFixedArrayLength()),
                        cache_array_false, efalse, if_false);
                }

                // Rewrite the uses of the {node}.
                control = graph()->NewNode(common()->Merge(2), if_true, if_false);
                effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
                cache_array = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                    cache_array_true, cache_array_false, control);
                cache_length = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
                    cache_length_true, cache_length_false, control);
                break;
            }
            }

            // Update the uses of {node}.
            for (Edge edge : node->use_edges()) {
                Node* const user = edge.from();
                if (NodeProperties::IsEffectEdge(edge)) {
                    edge.UpdateTo(effect);
                    Revisit(user);
                } else if (NodeProperties::IsControlEdge(edge)) {
                    edge.UpdateTo(control);
                    Revisit(user);
                } else {
                    DCHECK(NodeProperties::IsValueEdge(edge));
                    switch (ProjectionIndexOf(user->op())) {
                    case 0:
                        Replace(user, cache_type);
                        break;
                    case 1:
                        Replace(user, cache_array);
                        break;
                    case 2:
                        Replace(user, cache_length);
                        break;
                    default:
                        UNREACHABLE();
                    }
                }
            }
            node->Kill();
            return Replace(effect);
        }

        Reduction JSTypedLowering::ReduceJSLoadMessage(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSLoadMessage, node->opcode());
            ExternalReference const ref = ExternalReference::address_of_pending_message_obj(isolate());
            node->ReplaceInput(0, jsgraph()->ExternalConstant(ref));
            NodeProperties::ChangeOp(node, simplified()->LoadMessage());
            return Changed(node);
        }

        Reduction JSTypedLowering::ReduceJSStoreMessage(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSStoreMessage, node->opcode());
            ExternalReference const ref = ExternalReference::address_of_pending_message_obj(isolate());
            Node* value = NodeProperties::GetValueInput(node, 0);
            node->ReplaceInput(0, jsgraph()->ExternalConstant(ref));
            node->ReplaceInput(1, value);
            NodeProperties::ChangeOp(node, simplified()->StoreMessage());
            return Changed(node);
        }

        Reduction JSTypedLowering::ReduceJSGeneratorStore(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSGeneratorStore, node->opcode());
            Node* generator = NodeProperties::GetValueInput(node, 0);
            Node* continuation = NodeProperties::GetValueInput(node, 1);
            Node* offset = NodeProperties::GetValueInput(node, 2);
            Node* context = NodeProperties::GetContextInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            int value_count = GeneratorStoreValueCountOf(node->op());

            FieldAccess array_field = AccessBuilder::ForJSGeneratorObjectParametersAndRegisters();
            FieldAccess context_field = AccessBuilder::ForJSGeneratorObjectContext();
            FieldAccess continuation_field = AccessBuilder::ForJSGeneratorObjectContinuation();
            FieldAccess input_or_debug_pos_field = AccessBuilder::ForJSGeneratorObjectInputOrDebugPos();

            Node* array = effect = graph()->NewNode(simplified()->LoadField(array_field),
                generator, effect, control);

            for (int i = 0; i < value_count; ++i) {
                Node* value = NodeProperties::GetValueInput(node, 3 + i);
                if (value != jsgraph()->OptimizedOutConstant()) {
                    effect = graph()->NewNode(
                        simplified()->StoreField(AccessBuilder::ForFixedArraySlot(i)), array,
                        value, effect, control);
                }
            }

            effect = graph()->NewNode(simplified()->StoreField(context_field), generator,
                context, effect, control);
            effect = graph()->NewNode(simplified()->StoreField(continuation_field),
                generator, continuation, effect, control);
            effect = graph()->NewNode(simplified()->StoreField(input_or_debug_pos_field),
                generator, offset, effect, control);

            ReplaceWithValue(node, effect, effect, control);
            return Changed(effect);
        }

        Reduction JSTypedLowering::ReduceJSGeneratorRestoreContinuation(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSGeneratorRestoreContinuation, node->opcode());
            Node* generator = NodeProperties::GetValueInput(node, 0);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            FieldAccess continuation_field = AccessBuilder::ForJSGeneratorObjectContinuation();

            Node* continuation = effect = graph()->NewNode(
                simplified()->LoadField(continuation_field), generator, effect, control);
            Node* executing = jsgraph()->Constant(JSGeneratorObject::kGeneratorExecuting);
            effect = graph()->NewNode(simplified()->StoreField(continuation_field),
                generator, executing, effect, control);

            ReplaceWithValue(node, continuation, effect, control);
            return Changed(continuation);
        }

        Reduction JSTypedLowering::ReduceJSGeneratorRestoreContext(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSGeneratorRestoreContext, node->opcode());

            const Operator* new_op = simplified()->LoadField(AccessBuilder::ForJSGeneratorObjectContext());

            // Mutate the node in-place.
            DCHECK(OperatorProperties::HasContextInput(node->op()));
            DCHECK(!OperatorProperties::HasContextInput(new_op));
            node->RemoveInput(NodeProperties::FirstContextIndex(node));

            NodeProperties::ChangeOp(node, new_op);
            return Changed(node);
        }

        Reduction JSTypedLowering::ReduceJSGeneratorRestoreRegister(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSGeneratorRestoreRegister, node->opcode());
            Node* generator = NodeProperties::GetValueInput(node, 0);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);
            int index = RestoreRegisterIndexOf(node->op());

            FieldAccess array_field = AccessBuilder::ForJSGeneratorObjectParametersAndRegisters();
            FieldAccess element_field = AccessBuilder::ForFixedArraySlot(index);

            Node* array = effect = graph()->NewNode(simplified()->LoadField(array_field),
                generator, effect, control);
            Node* element = effect = graph()->NewNode(
                simplified()->LoadField(element_field), array, effect, control);
            Node* stale = jsgraph()->StaleRegisterConstant();
            effect = graph()->NewNode(simplified()->StoreField(element_field), array,
                stale, effect, control);

            ReplaceWithValue(node, element, effect, control);
            return Changed(element);
        }

        Reduction JSTypedLowering::ReduceJSGeneratorRestoreInputOrDebugPos(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSGeneratorRestoreInputOrDebugPos, node->opcode());

            FieldAccess input_or_debug_pos_field = AccessBuilder::ForJSGeneratorObjectInputOrDebugPos();
            const Operator* new_op = simplified()->LoadField(input_or_debug_pos_field);

            // Mutate the node in-place.
            DCHECK(OperatorProperties::HasContextInput(node->op()));
            DCHECK(!OperatorProperties::HasContextInput(new_op));
            node->RemoveInput(NodeProperties::FirstContextIndex(node));

            NodeProperties::ChangeOp(node, new_op);
            return Changed(node);
        }

        Reduction JSTypedLowering::ReduceObjectIsArray(Node* node)
        {
            Node* value = NodeProperties::GetValueInput(node, 0);
            Type value_type = NodeProperties::GetType(value);
            Node* context = NodeProperties::GetContextInput(node);
            Node* frame_state = NodeProperties::GetFrameStateInput(node);
            Node* effect = NodeProperties::GetEffectInput(node);
            Node* control = NodeProperties::GetControlInput(node);

            // Constant-fold based on {value} type.
            if (value_type.Is(Type::Array())) {
                Node* value = jsgraph()->TrueConstant();
                ReplaceWithValue(node, value);
                return Replace(value);
            } else if (!value_type.Maybe(Type::ArrayOrProxy())) {
                Node* value = jsgraph()->FalseConstant();
                ReplaceWithValue(node, value);
                return Replace(value);
            }

            int count = 0;
            Node* values[5];
            Node* effects[5];
            Node* controls[4];

            // Check if the {value} is a Smi.
            Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value);
            control = graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);

            // The {value} is a Smi.
            controls[count] = graph()->NewNode(common()->IfTrue(), control);
            effects[count] = effect;
            values[count] = jsgraph()->FalseConstant();
            count++;

            control = graph()->NewNode(common()->IfFalse(), control);

            // Load the {value}s instance type.
            Node* value_map = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForMap()), value, effect, control);
            Node* value_instance_type = effect = graph()->NewNode(
                simplified()->LoadField(AccessBuilder::ForMapInstanceType()), value_map,
                effect, control);

            // Check if the {value} is a JSArray.
            check = graph()->NewNode(simplified()->NumberEqual(), value_instance_type,
                jsgraph()->Constant(JS_ARRAY_TYPE));
            control = graph()->NewNode(common()->Branch(), check, control);

            // The {value} is a JSArray.
            controls[count] = graph()->NewNode(common()->IfTrue(), control);
            effects[count] = effect;
            values[count] = jsgraph()->TrueConstant();
            count++;

            control = graph()->NewNode(common()->IfFalse(), control);

            // Check if the {value} is a JSProxy.
            check = graph()->NewNode(simplified()->NumberEqual(), value_instance_type,
                jsgraph()->Constant(JS_PROXY_TYPE));
            control = graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);

            // The {value} is neither a JSArray nor a JSProxy.
            controls[count] = graph()->NewNode(common()->IfFalse(), control);
            effects[count] = effect;
            values[count] = jsgraph()->FalseConstant();
            count++;

            control = graph()->NewNode(common()->IfTrue(), control);

            // Let the %ArrayIsArray runtime function deal with the JSProxy {value}.
            value = effect = control = graph()->NewNode(javascript()->CallRuntime(Runtime::kArrayIsArray), value,
                context, frame_state, effect, control);
            NodeProperties::SetType(value, Type::Boolean());

            // Update potential {IfException} uses of {node} to point to the above
            // %ArrayIsArray runtime call node instead.
            Node* on_exception = nullptr;
            if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
                NodeProperties::ReplaceControlInput(on_exception, control);
                NodeProperties::ReplaceEffectInput(on_exception, effect);
                control = graph()->NewNode(common()->IfSuccess(), control);
                Revisit(on_exception);
            }

            // The {value} is neither a JSArray nor a JSProxy.
            controls[count] = control;
            effects[count] = effect;
            values[count] = value;
            count++;

            control = graph()->NewNode(common()->Merge(count), count, controls);
            effects[count] = control;
            values[count] = control;
            effect = graph()->NewNode(common()->EffectPhi(count), count + 1, effects);
            value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, count),
                count + 1, values);
            ReplaceWithValue(node, value, effect, control);
            return Replace(value);
        }

        Reduction JSTypedLowering::ReduceJSParseInt(Node* node)
        {
            Node* value = NodeProperties::GetValueInput(node, 0);
            Type value_type = NodeProperties::GetType(value);
            Node* radix = NodeProperties::GetValueInput(node, 1);
            Type radix_type = NodeProperties::GetType(radix);
            // We need kTenOrUndefined and kZeroOrUndefined because
            // the type representing {0,10} would become the range 1-10.
            if (value_type.Is(type_cache_->kSafeInteger) && (radix_type.Is(type_cache_->kTenOrUndefined) || radix_type.Is(type_cache_->kZeroOrUndefined))) {
                // Number.parseInt(a:safe-integer) -> a
                // Number.parseInt(a:safe-integer,b:#0\/undefined) -> a
                // Number.parseInt(a:safe-integer,b:#10\/undefined) -> a
                ReplaceWithValue(node, value);
                return Replace(value);
            }
            return NoChange();
        }

        Reduction JSTypedLowering::ReduceJSResolvePromise(Node* node)
        {
            DCHECK_EQ(IrOpcode::kJSResolvePromise, node->opcode());
            Node* resolution = NodeProperties::GetValueInput(node, 1);
            Type resolution_type = NodeProperties::GetType(resolution);
            // We can strength-reduce JSResolvePromise to JSFulfillPromise
            // if the {resolution} is known to be a primitive, as in that
            // case we don't perform the implicit chaining (via "then").
            if (resolution_type.Is(Type::Primitive())) {
                // JSResolvePromise(p,v:primitive) -> JSFulfillPromise(p,v)
                node->RemoveInput(3); // frame state
                NodeProperties::ChangeOp(node, javascript()->FulfillPromise());
                return Changed(node);
            }
            return NoChange();
        }

        Reduction JSTypedLowering::Reduce(Node* node)
        {
            DisallowHeapAccess no_heap_access;

            switch (node->opcode()) {
            case IrOpcode::kJSEqual:
                return ReduceJSEqual(node);
            case IrOpcode::kJSStrictEqual:
                return ReduceJSStrictEqual(node);
            case IrOpcode::kJSLessThan: // fall through
            case IrOpcode::kJSGreaterThan: // fall through
            case IrOpcode::kJSLessThanOrEqual: // fall through
            case IrOpcode::kJSGreaterThanOrEqual:
                return ReduceJSComparison(node);
            case IrOpcode::kJSBitwiseOr:
            case IrOpcode::kJSBitwiseXor:
            case IrOpcode::kJSBitwiseAnd:
                return ReduceInt32Binop(node);
            case IrOpcode::kJSShiftLeft:
            case IrOpcode::kJSShiftRight:
                return ReduceUI32Shift(node, kSigned);
            case IrOpcode::kJSShiftRightLogical:
                return ReduceUI32Shift(node, kUnsigned);
            case IrOpcode::kJSAdd:
                return ReduceJSAdd(node);
            case IrOpcode::kJSSubtract:
            case IrOpcode::kJSMultiply:
            case IrOpcode::kJSDivide:
            case IrOpcode::kJSModulus:
            case IrOpcode::kJSExponentiate:
                return ReduceNumberBinop(node);
            case IrOpcode::kJSBitwiseNot:
                return ReduceJSBitwiseNot(node);
            case IrOpcode::kJSDecrement:
                return ReduceJSDecrement(node);
            case IrOpcode::kJSIncrement:
                return ReduceJSIncrement(node);
            case IrOpcode::kJSNegate:
                return ReduceJSNegate(node);
            case IrOpcode::kJSHasInPrototypeChain:
                return ReduceJSHasInPrototypeChain(node);
            case IrOpcode::kJSOrdinaryHasInstance:
                return ReduceJSOrdinaryHasInstance(node);
            case IrOpcode::kJSToLength:
                return ReduceJSToLength(node);
            case IrOpcode::kJSToName:
                return ReduceJSToName(node);
            case IrOpcode::kJSToNumber:
            case IrOpcode::kJSToNumberConvertBigInt:
                return ReduceJSToNumber(node);
            case IrOpcode::kJSToNumeric:
                return ReduceJSToNumeric(node);
            case IrOpcode::kJSToString:
                return ReduceJSToString(node);
            case IrOpcode::kJSToObject:
                return ReduceJSToObject(node);
            case IrOpcode::kJSLoadNamed:
                return ReduceJSLoadNamed(node);
            case IrOpcode::kJSLoadContext:
                return ReduceJSLoadContext(node);
            case IrOpcode::kJSStoreContext:
                return ReduceJSStoreContext(node);
            case IrOpcode::kJSLoadModule:
                return ReduceJSLoadModule(node);
            case IrOpcode::kJSStoreModule:
                return ReduceJSStoreModule(node);
            case IrOpcode::kJSConstructForwardVarargs:
                return ReduceJSConstructForwardVarargs(node);
            case IrOpcode::kJSConstruct:
                return ReduceJSConstruct(node);
            case IrOpcode::kJSCallForwardVarargs:
                return ReduceJSCallForwardVarargs(node);
            case IrOpcode::kJSCall:
                return ReduceJSCall(node);
            case IrOpcode::kJSForInPrepare:
                return ReduceJSForInPrepare(node);
            case IrOpcode::kJSForInNext:
                return ReduceJSForInNext(node);
            case IrOpcode::kJSLoadMessage:
                return ReduceJSLoadMessage(node);
            case IrOpcode::kJSStoreMessage:
                return ReduceJSStoreMessage(node);
            case IrOpcode::kJSGeneratorStore:
                return ReduceJSGeneratorStore(node);
            case IrOpcode::kJSGeneratorRestoreContinuation:
                return ReduceJSGeneratorRestoreContinuation(node);
            case IrOpcode::kJSGeneratorRestoreContext:
                return ReduceJSGeneratorRestoreContext(node);
            case IrOpcode::kJSGeneratorRestoreRegister:
                return ReduceJSGeneratorRestoreRegister(node);
            case IrOpcode::kJSGeneratorRestoreInputOrDebugPos:
                return ReduceJSGeneratorRestoreInputOrDebugPos(node);
            case IrOpcode::kJSObjectIsArray:
                return ReduceObjectIsArray(node);
            case IrOpcode::kJSParseInt:
                return ReduceJSParseInt(node);
            case IrOpcode::kJSResolvePromise:
                return ReduceJSResolvePromise(node);
            default:
                break;
            }
            return NoChange();
        }

        Factory* JSTypedLowering::factory() const { return jsgraph()->factory(); }

        Graph* JSTypedLowering::graph() const { return jsgraph()->graph(); }

        Isolate* JSTypedLowering::isolate() const { return jsgraph()->isolate(); }

        JSOperatorBuilder* JSTypedLowering::javascript() const
        {
            return jsgraph()->javascript();
        }

        CommonOperatorBuilder* JSTypedLowering::common() const
        {
            return jsgraph()->common();
        }

        SimplifiedOperatorBuilder* JSTypedLowering::simplified() const
        {
            return jsgraph()->simplified();
        }

    } // namespace compiler
} // namespace internal
} // namespace v8
